/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package sintatico;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Stack;
import java.util.ArrayList;
import org.antlr.runtime.ANTLRFileStream;
import org.antlr.runtime.Token;
import lexico.AnalisadorLexico;
import semantico.*;
import programa.SimpleToken;

/**
 *
 * @author Gabriel, Lucas, Rodrigo
 */
public class AnalisadorSintatico {

    private AnalisadorLexico lex;
    private Token token;
    private Stack<Escopo> pe;
    private ArrayList<Declaracao> ld;
    private Declaracao decl;
    private String outputFilePath;
    public static BufferedWriter out;
    public static FileWriter fstream;
    private StringBuilder sb;
    public boolean erro = false;
    public boolean erroSemantico = false;
    /* ID da regra */
    public static final int PROGRAMA = 1;
    public static final int DECLARACAO_LOCAL = 2;
    public static final int VARIAVEL = 3;
    public static final int IDENTIFICADOR = 4;
    public static final int TIPO_BASICO = 5;
    public static final int TIPO = 6;
    public static final int VALOR_CONSTANTE = 7;
    public static final int REGISTRO = 8;
    public static final int DECLARACAO_GLOBAL = 9;
    public static final int PARAMETRO = 10;
    public static final int CMD = 11;
    public static final int EXPRESSAO = 12;
    public static final int EXP_RELACIONAL = 13;
    public static final int TERMO_LOGICO = 14;
    public static final int CONSTANTES = 15;
    public static final int EXP_ARITMETICA = 16;
    public static final int TERMO = 17;
    public static final int FATOR = 18;
    public static final int PARCELA_UNARIO = 19;
    public static final int PARCELA_NAO_UNARIO = 20;

    public AnalisadorSintatico() {
    }

    public AnalisadorSintatico(String inputFilePath, String outputFilePath) {
        this.pe = new Stack<Escopo>();
        this.ld = new ArrayList<Declaracao>();
        this.decl = new Declaracao();
        this.sb = new StringBuilder();
        this.outputFilePath = outputFilePath;
        try {
            this.lex = new AnalisadorLexico(new ANTLRFileStream(inputFilePath));
        } catch (IOException e) {
        }
        try {
            // Cria arquivo de saída
            fstream = new FileWriter(outputFilePath);
            out = new BufferedWriter(fstream);
        } catch (IOException e) {
        }
    }

    public Token getToken() {
        token = lex.nextToken();
        switch (token.getType()) {
            case SimpleToken.ESPACO_BRANCO:
            case SimpleToken.COMENTARIO:
                getToken();
                break;
            case SimpleToken.ERRO:
            case SimpleToken.ERRO2:
                erro();
            case SimpleToken.EOF:
                break;
            default:
        }
        return token;
    }

    /*
     * verifica se token pertence ao conjunto dos primeiros da regra
     */
    public boolean primeiro(int regraID) {
        switch (regraID) {
            case PROGRAMA:
                if (primeiro(DECLARACAO_LOCAL)
                        | primeiro(DECLARACAO_GLOBAL)
                        | token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "algoritmo"))) {
                    return true;
                } else {
                    return false;
                }
            case DECLARACAO_LOCAL:
                if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "declare"))
                        | token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "constante"))
                        | token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "tipo"))) {
                    return true;
                } else {
                    return false;
                }
            case VARIAVEL:
            case IDENTIFICADOR:
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "^")) | token.getType() == SimpleToken.IDENTIFICADOR) {
                    return true;
                } else {
                    return false;
                }
            case TIPO:
                if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "registro"))
                        | token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "^"))
                        | primeiro(TIPO_BASICO)
                        | token.getType() == SimpleToken.IDENTIFICADOR) {
                    return true;
                } else {
                    return false;
                }
            case TIPO_BASICO:
                if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "literal"))
                        | token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "inteiro"))
                        | token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "real"))
                        | token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "logico"))) {
                    return true;
                } else {
                    return false;
                }
            case VALOR_CONSTANTE:
                if (token.getType() == SimpleToken.CADEIA_LITERAL
                        | token.getType() == SimpleToken.NUMERO_INTEIRO
                        | token.getType() == SimpleToken.NUMERO_REAL
                        | token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "verdadeiro"))
                        | token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "falso"))) {
                    return true;
                } else {
                    return false;
                }
            case REGISTRO:
                if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "registro"))) {
                    return true;
                } else {
                    return false;
                }
            case DECLARACAO_GLOBAL:
                if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "procedimento"))
                        | token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "funcao"))) {
                    return true;
                } else {
                    return false;
                }
            case PARAMETRO:
                if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "var")) | primeiro(IDENTIFICADOR)) {
                    return true;
                } else {
                    return false;
                }
            case CMD:
                if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "leia"))
                        | token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "escreva"))
                        | token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "se"))
                        | token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "caso"))
                        | token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "para"))
                        | token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "enquanto"))
                        | token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "faca"))
                        | token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "^"))
                        | token.getType() == SimpleToken.IDENTIFICADOR
                        | token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "retorne"))) {
                    return true;
                } else {
                    return false;
                }
            case EXPRESSAO:
            case TERMO_LOGICO:
                if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "nao"))
                        | token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "verdadeiro"))
                        | token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "falso"))
                        | primeiro(EXP_RELACIONAL)) {
                    return true;
                } else {
                    return false;
                }
            case CONSTANTES:
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "-"))
                        | token.getType() == SimpleToken.NUMERO_INTEIRO) {
                    return true;
                } else {
                    return false;
                }
            case EXP_RELACIONAL:
            case EXP_ARITMETICA:
            case TERMO:
            case FATOR:
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "-"))
                        | primeiro(PARCELA_UNARIO)
                        | primeiro(PARCELA_NAO_UNARIO)) {
                    return true;
                } else {
                    return false;
                }
            case PARCELA_UNARIO:
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "^"))
                        | token.getType() == SimpleToken.IDENTIFICADOR
                        | token.getType() == SimpleToken.NUMERO_INTEIRO
                        | token.getType() == SimpleToken.NUMERO_REAL
                        | token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "("))) {
                    return true;
                } else {
                    return false;
                }
            case PARCELA_NAO_UNARIO:
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "&"))
                        | token.getType() == SimpleToken.CADEIA_LITERAL) {
                    return true;
                } else {
                    return false;
                }
            default:
                return false;
        }
    }

    /* LINGUAGEM LA */
    /*
     * <programa> ::= <declaracoes> algoritmo <corpo> fim_algoritmo
     * 
     */
    public void programa() {
        pe.push(new Escopo(lex.getLine(), "programa", "inteiro")); //analise semantica: empilha escopo do programa       
        includeBasicos();
        getToken();
        geraCodigoHeader(); // geracao codigo C: #include <libs>
        // <declaracoes>
        while (primeiro(DECLARACAO_LOCAL) | primeiro(DECLARACAO_GLOBAL)) {
            decl = new Declaracao();
            ld = new ArrayList<Declaracao>();
            if (primeiro(DECLARACAO_LOCAL)) {
                declaracao_local();
                insereDeclaracaoEscopoGlobal(); // analise semantica: insere declaracoes na tabela simbolos com escopo "global"
            } else if (primeiro(DECLARACAO_GLOBAL)) {
                declaracao_global(); // analise semantica: declaracao global eh inserida automaticamente
            } else {
                erro();
            }
        }
        // algoritmo <corpo> fim_algoritmo
        if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "algoritmo"))) {
            getToken();
            sb.append("\nint main() {\n\n");
            corpo();
            if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "fim_algoritmo"))) {
                getToken();
                sb.append("\nreturn 0;\n");
                sb.append("}");
            } else {
                erro();
            }
        } else {
            erro();
        }
        try {
            if (!(erro | erroSemantico)) {
                File f = new File(outputFilePath);
                f.delete();
                fstream = new FileWriter(outputFilePath + ".c");
                out = new BufferedWriter(fstream);
                out.write(sb.toString());
                //out.newLine();
                out.close();
            } else {
                out.write("Fim da Compilacao");
                out.newLine();
                out.close();
            }
        } catch (Exception exc) {
            System.out.println("ERRO: " + exc.getMessage());
        }
    }

    /*
     * <declaracao_local> ::= declare <variavel> | constante IDENT : <tipo_basico> = <valor_constante> | tipo IDENT : <tipo>
     */
    public void declaracao_local() {
        if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "declare"))) {
            // analise semantica: atribuicao escopo e categoria para Declaracao
            decl.setEscopo("local");
            decl.setCategoria("variavel");
            // fim analise semantica
            // declare <variavel>
            getToken();
            variavel();
        } else if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "constante"))) {
            // analise semantica: atribuicao escopo e categoria para Declaracao
            decl.setEscopo("local");
            decl.setCategoria("constante");
            // fim analise semantica
            // | constante IDENT : <tipo_basico> = <valor_constante>            
            getToken();
            if (token.getType() == SimpleToken.IDENTIFICADOR) {
                // analise semantica: atribuicao identificador e linha para Declaracao
                decl.setIdentificador(token.getText());
                decl.setLinha(token.getLine());
                // fim analise semantica
                getToken();
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ":"))) {
                    getToken();
                    tipo_basico();  // analise semantica: atribuicao de tipo para Declaracao
                    if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "="))) {
                        getToken();
                        // analise semantica: copia atual Declaracao para tmp, decl ira conter a declaracao em valor_constante()
                        Declaracao tmp = decl.clone();
                        decl = new Declaracao();
                        valor_constante();
                        if (tmp.getTipo().equals(decl.getTipo())) { // se tipo Declaracao = tipo atribuicao
                            tmp.setValor(decl.getValor()); // atualiza valor Declaracao
                            decl = tmp.clone();
                        } else {
                            erroSemantico(Declaracao.ERRO_SEMANTICO_5); // erro semantico: tipos incompativeis na atribuicao
                        }
                        // fim analise semantica
                    } else {
                        erro();
                    }
                } else {
                    erro();
                }
            } else {
                erro();
            }
        } else if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "tipo"))) {
            // analise semantica: atribuicao escopo e categoria para Declaracao
            decl.setEscopo("local");
            decl.setCategoria("tipo");
            // fim analise semantica
            // | tipo IDENT : <tipo>
            getToken();
            if (token.getType() == SimpleToken.IDENTIFICADOR) {
                // analise semantica: atribuicao identificador e linha para Declaracao
                decl.setIdentificador(token.getText());
                decl.setLinha(token.getLine());
                // fim analise semantica
                getToken();
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ":"))) {
                    getToken();
                    tipo(); // analise semantica: atribuicao de tipo para Declaracao
                } else {
                    erro();
                }
            } else {
                erro();
            }
        } else {
            erro();
        }
    }

    /*
     * <variavel> ::= <identificador> <mais_ident> : <tipo>
     */
    public void variavel() {
        // <identificador>
        identificador(); // analise semantica: atribuicao do identificador da Declaracao em decl
        //<mais_ident>
        if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ","))) {
            ld.add(decl); // armazena declaracoes na lista
            do {
                decl = new Declaracao();
                decl.setEscopo("local");
                getToken();
                identificador();
                ld.add(decl);
            } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ",")));
        }
        // : <tipo>
        if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ":"))) {
            getToken();
            tipo(); // analise semantica: atribuicao de tipo para Declaracao
            if (!ld.isEmpty()) { // se lista de declaracoes nao esta vazia, atribuicao de tipo para todas as declaracoes (tipo definido em decl)
                for (int i = 0; i < ld.size(); i++) {
                    ld.get(i).setTipo(decl.getTipo());
                }
            }
        } else {
            erro();
        }

    }

    /*
     * <identificador> ::= <ponteiro_opcional> IDENT <outros_ident> <dimensao>
     */
    public void identificador() {
        // <ponteiro_opcional> 
        if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "^"))) {
            decl.setPtr(true); // analise semantica: atribuicao de ponteiro para Declaracao
            getToken();
        }
        // IDENT 
        if (token.getType() == SimpleToken.IDENTIFICADOR) {
            decl.setIdentificador(token.getText()); // analise semantica: atribuicao de identificador para Declaracao
            Declaracao tmp = decl.clone(); // guarda Declaracao atual
            if (!procuraDeclaracao(0)) { // verifica se identificador ja foi declarado, nao gera erro
                decl = tmp.clone();
            }
            decl.setLinha(token.getLine()); // atribuicao de linha para Declaracao
            // fim analise semantica
            getToken();
            // <outros_ident>
            if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "."))) {
                String declIdent = decl.getIdentificador(); // guarda identificador da declaracao do tipo registro
                do {
                    declIdent = declIdent + token.getText();
                    getToken();
                    if (token.getType() == SimpleToken.IDENTIFICADOR) {
                        // analise semantica: acesso a campos do registro
                        declIdent = declIdent + token.getText(); // guarda identificador do campo do registro                        
                        tmp = new Declaracao();
                        tmp.setEscopo("local");
                        tmp.setIdentificador(token.getText());
                        tmp.setLinha(token.getLine());
                        if (!decl.getDecLocais().contains(tmp)) { // se registro nao contem declaracao
                            decl.setIdentificador(declIdent);
                            erroSemantico(Declaracao.ERRO_SEMANTICO_3); // erro semantico: variável não declarada no registro
                            decl.setErro(true);
                        } else {
                            decl = decl.getDecLocais().get(decl.getDecLocais().indexOf(tmp)).clone();
                            decl.setIdentificador(declIdent.split("\\.")[0]);
                            decl.setValor(declIdent);
                            decl.setLinha(token.getLine());
                        }
                        // fim analise semantica
                        getToken();
                    } else {
                        erro();
                    }
                } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ".")));
            }
            // <dimensao>
            if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "["))) {
                decl.setArray(true); // analise semantica: atribuicao de vetor para Declaracao
                tmp = decl.clone(); // guarda declaracao atual
                do {
                    getToken();
                    decl = new Declaracao();
                    exp_aritmetica(); // definicao da dimensao em decl
                    tmp.getDimensao().add(decl); // armazena dimensao da declaracao
                    if (tmp.getIdentificador().isEmpty()) {
                        tmp.setIdentificador(tmp.getValor());
                    }
                    //tmp.setValor(tmp.getIdentificador().trim() + "[" + decl.getValor().trim() + "]");
                    if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "]"))) {
                        getToken();
                    } else {
                        erro();
                    }
                } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "[")));
                decl = tmp.clone(); // recupera Declaracao em decl
            }
        } else {
            erro();
        }
    }

    /*
     * <tipo_basico> ::= literal | inteiro | real | logico
     */
    public void tipo_basico() {
        // literal | inteiro | real | logico
        if (primeiro(TIPO_BASICO)) {
            decl.setTipo(token.getText()); // analise semantica: atribuicao de tipo para Declaracao
            getToken();
        } else {
            erro();
        }
    }

    /*
     * <tipo> ::= <registro> | <tipo_estendido>
     */
    public void tipo() {
        // <registro>
        if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "registro"))) {
            // analise semantica: declaracao do tipo registro
            decl.setTipo("registro");
            if (!ld.isEmpty()) { // se lista de declaracoes nao esta vazia, contem declaracoes do tipo registro
                ArrayList<Declaracao> tmpLd = new ArrayList<Declaracao>();
                tmpLd.addAll(ld); // guarda declaracoes atuais de registro
                ld.clear(); // limpa ld, que ira conter declaracoes locais do registro
                registro(); // definicao do tipo registro com suas declaracoes em decl
                ld.clear(); // limpa lista que contem declaracoes locais do registro, tais declaracoes jah existem em decl
                ld.addAll(tmpLd); // recupera declaracoes de registro
                for (int i = 0; i < ld.size(); i++) { // atualiza as declaracoes locais dos registros
                    if (ld.get(i).getDecLocais().isEmpty()) {
                        ld.get(i).getDecLocais().addAll(decl.getDecLocais());
                    }
                }
            } else {
                registro(); // apenas uma declaracao do tipo registro (decl)
            }
            // fim analise semantica
        } else {
            // | <tipo_estendido>
            if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "^"))) {
                decl.setPtr(true); // analise semantica: atribuicao de ponteiro               
                getToken();
            }
            if (primeiro(TIPO_BASICO) | token.getType() == SimpleToken.IDENTIFICADOR) {
                // analise semantica: tipo da Declaracao
                if (token.getType() == SimpleToken.IDENTIFICADOR) {
                    Declaracao tmp = decl.clone(); // guarda declaracao atual
                    decl = new Declaracao();
                    decl.setIdentificador(token.getText());
                    decl.setLinha(tmp.getLinha());
                    procuraDeclaracao(Declaracao.ERRO_SEMANTICO_2); // verifica se tipo ja foi declarado, se existe eh copiado para decl senao erro de tipo nao declarado
                    if (ld.isEmpty()) { // se lista de declaracoes esta vazia, copia declaracoes locais do tipo registro para Declaracao
                        tmp.getDecLocais().addAll(decl.getDecLocais());
                    } else {
                        for (int i = 0; i < ld.size(); i++) { // copia declaracoes locais do tipo registro para todas declaracoes da lista
                            if (ld.get(i).getDecLocais().isEmpty()) {
                                ld.get(i).getDecLocais().addAll(decl.getDecLocais());
                            }
                        }
                    }
                    decl = tmp.clone(); // atualiza Declaracao em decl
                }
                decl.setTipo(token.getText()); // atribuicao de tipo para Declaracao
                // fim analise semantica
                getToken();
            } else {
                erro();
            }
        }
    }

    /*
     * <valor_constante> ::= CADEIA | NUM_INT | NUM_REAL | verdadeiro | falso
     */
    public void valor_constante() {
        // CADEIA | NUM_INT | NUM_REAL | verdadeiro | falso
        if (primeiro(VALOR_CONSTANTE)) {
            // analise semantica: atribuicao tipo e valor da declaracao
            setTipoBasicoDeclaracao(token);
            decl.setValor(token.getText());
            // fim AS
            getToken();
        } else {
            erro();
        }
    }

    /*
     * <registro> ::= registro <variavel> <mais_variaveis> fim_registro
     */
    public void registro() {
        // registro
        if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "registro"))) {
            Declaracao tmp = decl.clone(); // analise semantica: guarda Declaracao atual
            getToken();
            // <variavel> <mais_variaveis>
            do {
                // analise semantica: definicoes de campos do registro
                decl = new Declaracao();
                decl.setEscopo("local");
                decl.setTipo("registro");
                ld.clear(); // limpa lista de declaracoes, ira conter campos do registro
                variavel();
                if (ld.isEmpty()) { // se lista esta vazia, campo do registro definido em decl
                    if (tmp.getDecLocais().contains(decl)) {
                        erroSemantico(Declaracao.ERRO_SEMANTICO_1); // erro semantico: campo do registro jah declarado
                    } else {
                        if (tmp.isPtr()) {
                            decl.setPtr(true);
                        }
                        tmp.getDecLocais().add(decl); // adiciona campo ao registro
                    }
                } else {
                    for (int i = 0; i < ld.size(); i++) { // ld contem mais de um campo do mesmo tipo
                        if (tmp.getDecLocais().contains(ld.get(i))) {
                            erroSemantico(Declaracao.ERRO_SEMANTICO_1); // erro semantico: campo do registro jah declarado
                        } else {
                            if (tmp.isPtr()) {
                                ld.get(i).setPtr(true);
                            }
                            tmp.getDecLocais().add(ld.get(i)); // adiciona campo ao registro
                        }
                    }
                }
                // fim analise semantica
            } while (primeiro(VARIAVEL));
            decl = tmp.clone(); // recupera registro em decl
            if (decl.getCategoria().equals("tipo")) {
                ld.clear(); // se declaracao eh um tipo, limpa lista de declaracoes que contem campos do tipo registro
            }
            // fim_registro
            if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "fim_registro"))) {
                getToken();
            } else {
                erro();
            }
        } else {
            erro();
        }
    }

    /*
     * <declaracao_global> ::= procedimento IDENT ( <parametros_opcional> ) <declaracoes_locais> <comandos> fim_procedimento 
     * | funcao IDENT ( <parametros_opcional> ) : <tipo_estendido> <declaracoes_locais> <comandos> fim_funcao
     */
    public void declaracao_global() {
        // procedimento IDENT
        if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "procedimento"))) {
            // analise semantica: definicao da declaracao global procedimento
            decl.setEscopo("global");
            decl.setCategoria("procedimento");
            getToken();
            if (token.getType() == SimpleToken.IDENTIFICADOR) {
                // analise semantica: identificador do procedimento e linha declarada
                decl.setIdentificador(token.getText());
                decl.setLinha(token.getLine());
                getToken();
                // ( <parametros_opcional> )
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "("))) {
                    getToken();
                    if (primeiro(PARAMETRO)) {
                        parametro(); // analise semantica: definicao de parametros do procedimento
                    }
                    if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ")"))) {
                        getToken();
                        // <declaracoes_locais> <comandos> fim_procedimento 
                        Declaracao tmp = decl.clone(); // guarda declaracao atual
                        while (primeiro(DECLARACAO_LOCAL)) {
                            decl = new Declaracao();
                            declaracao_local();
                            insereDeclaracaoEscopo(tmp); // analise semantica: insere declaracoes locais do procedimento
                        }
                        decl = tmp.clone(); // recupera declaracao
                        pe.firstElement().addDeclaracao(decl); // analise semantica: insere declaracao do procedimento bem formulada no escopo do programa
                        pe.push(new Escopo(decl.getLinha(), "procedimento", "")); // analise semantica: empilha escopo do procedimento para analise semantica dos comandos
                        insereParametroEscopo(); // copia parametros do procedimento para o novo escopo                        
                        geraCodigoProcedimento();
                        decl = tmp.clone();
                        if (!decl.getDecLocais().isEmpty()) { // se procedimento contem declaracoes locais
                            ld.clear();
                            ld.addAll(decl.getDecLocais()); // carrega declaracoes locais em ld
                            insereDeclaracaoEscopo(); // insere declaracoes locais do procedimento no novo escopo
                        }
                        while (primeiro(CMD)) {
                            cmd(); // analise semantica dos comandos do procedimento
                        }
                        if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "fim_procedimento"))) {
                            pe.lastElement().getDeclaracoes().clear(); // limpa o escopo do procedimento
                            pe.pop(); // desempilha escopo do procedimento
                            getToken();
                            sb.append("}\n");
                        } else {
                            erro();
                        }
                    } else {
                        erro();
                    }
                } else {
                    erro();
                }
            } else {
                erro();
            }
            // funcao IDENT
        } else if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "funcao"))) {
            // analise semantica: identificador da funcao e linha declarada
            decl.setEscopo("global");
            decl.setCategoria("funcao");
            getToken();
            if (token.getType() == SimpleToken.IDENTIFICADOR) {
                // analise semantica: identificador da funcao e linha declarada
                decl.setIdentificador(token.getText());
                decl.setLinha(token.getLine());
                getToken();
                // ( <parametros_opcional> )
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "("))) {
                    getToken();
                    if (primeiro(PARAMETRO)) {
                        parametro(); // analise semantica: definicao de parametros do procedimento
                    }
                    if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ")"))) {
                        getToken();
                        // :
                        if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ":"))) {
                            getToken();
                            // <tipo_estendido>
                            if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "^"))) {
                                decl.setPtr(true); // funcao retorna ponteiro
                                getToken();
                            }
                            if (primeiro(TIPO_BASICO) | token.getType() == SimpleToken.IDENTIFICADOR) {
                                if (token.getType() == SimpleToken.IDENTIFICADOR) {
                                    Declaracao tmp = decl.clone();
                                    decl = new Declaracao();
                                    decl.setIdentificador(token.getText());
                                    decl.setLinha(tmp.getLinha());
                                    procuraDeclaracao(Declaracao.ERRO_SEMANTICO_2); // verifica se tipo de retorno jah foi declarado
                                    decl = tmp.clone();
                                }
                                decl.setTipo(token.getText()); // atribuicao de tipo de retorno para funcao
                                getToken();
                                // <declaracoes_locais> <comandos> fim_funcao 
                                Declaracao tmp = decl.clone();
                                while (primeiro(DECLARACAO_LOCAL)) {
                                    decl = new Declaracao();
                                    declaracao_local();
                                    insereDeclaracaoEscopo(tmp); // analise semantica: insere declaracoes locais da funcao
                                }
                                decl = tmp.clone();
                                pe.firstElement().addDeclaracao(decl); // adiciona declaracao bem formulada da funcao
                                pe.push(new Escopo(decl.getLinha(), "funcao", decl.getTipo())); // empilha escopo da funcao
                                insereParametroEscopo(); // copia parametros da funcao para novo escopo
                                geraCodigoFuncao();
                                if (!decl.getDecLocais().isEmpty()) {
                                    ld = new ArrayList<Declaracao>();
                                    ld.addAll(decl.getDecLocais());
                                    insereDeclaracaoEscopo(); // copia declaracoes locais da funcao para novo escopo
                                }
                                while (primeiro(CMD)) {
                                    cmd(); // analise semantica dos comandos da funcao
                                }
                                if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "fim_funcao"))) {
                                    pe.lastElement().getDeclaracoes().clear(); // limpa escopo da funcao
                                    pe.pop(); // desempilha escopo da funcao
                                    getToken();
                                    sb.append("}\n");
                                } else {
                                    erro();
                                }
                            } else {
                                erro();
                            }
                        } else {
                            erro();
                        }
                    } else {
                        erro();
                    }
                } else {
                    erro();
                }
            } else {
                erro();
            }
        } else {
            erro();
        }
    }

    /*
     * <parametro> ::= <var_opcional> <identificador> <mais_ident> : <tipo_estendido> <mais_parametros>
     */
    public void parametro() {
        // <var_opcional>
        Declaracao tmp = decl.clone(); // guarda declaracao atual         
        decl = new Declaracao();
        decl.setEscopo("local");
        decl.setCategoria("parametro");
        if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "var"))) {
            decl.setPtr(true);
            getToken();
        }
        // <identificador> <mais_ident>
        identificador(); // analise semantica: identificador do parametro
        tmp.getParametro().add(decl); // analise semantica: adiciona parametro na lista de parametros da declaracao global
        if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ","))) {
            do {
                decl = new Declaracao();
                decl.setEscopo("local");
                decl.setCategoria("parametro");
                getToken();
                identificador();
                if (tmp.getParametro().contains(decl)) {
                    erroSemantico(Declaracao.ERRO_SEMANTICO_1); // erro semantico: parametro jah declarado 
                } else {
                    tmp.getParametro().add(decl); // analise semantica: adiciona parametro na lista de parametros da declaracao global
                }
            } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ",")));
        }
        // :
        if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ":"))) {
            getToken();
            // <tipo_estendido>
            if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "^"))) {
                for (int i = 0; i < tmp.getParametro().size(); i++) {
                    tmp.getParametro().get(i).setPtr(true); // atualiza tipo dos parametros da lista
                }
                getToken();
            }
            if (primeiro(TIPO_BASICO) | token.getType() == SimpleToken.IDENTIFICADOR) {
                for (int i = 0; i < tmp.getParametro().size(); i++) {
                    if (tmp.getParametro().get(i).getTipo().isEmpty()) {
                        if (token.getType() == SimpleToken.IDENTIFICADOR) {
                            Declaracao tmp2 = decl.clone(); // guarda parametro atual
                            decl = new Declaracao();
                            decl.setIdentificador(token.getText());
                            decl.setLinha(tmp.getLinha());
                            procuraDeclaracao(Declaracao.ERRO_SEMANTICO_2); // erro semantico: tipo nao definido
                            tmp.getParametro().get(i).getDecLocais().addAll(decl.getDecLocais()); // copia declaracoes locais do tipo registro para os parametros
                            if (tmp2.isPtr()) {
                                for (int j = 0; j < tmp.getParametro().get(i).getDecLocais().size(); j++) {
                                    tmp.getParametro().get(i).getDecLocais().get(j).setPtr(true);
                                }
                            }
                            decl = tmp2.clone();
                        }
                        tmp.getParametro().get(i).setTipo(token.getText()); // atribuicao de tipo para os parametros
                    }
                }
                getToken();
                decl = tmp.clone(); // recupera declaracao global em decl
                // <mais_parametros>
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ","))) {
                    do {
                        getToken();
                        parametro();
                    } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ",")));
                }
            } else {
                erro();
            }
        } else {
            erro();
        }
    }

    /*
     * <corpo> ::= <declaracoes_locais> <comandos>
     */
    public void corpo() {
        // <declaracoes_locais>
        while (primeiro(DECLARACAO_LOCAL)) {
            decl = new Declaracao();
            ld = new ArrayList<Declaracao>();
            declaracao_local(); // analise semantica: declaracoes locais do escopo principal 
            insereDeclaracaoEscopo(); // analise semantica: insere declaracoes locais no escopo principal
        }
        // <comandos>
        while (primeiro(CMD)) {
            cmd(); // analise semantica: comandos do escopo principal
        }
    }

    /*
     * <cmd> ::= leia ( <identificador> <mais_ident> )
     *           | escreva ( <expressao> <mais_expressao> )
     *           | se <expressao> entao <comandos> <senao_opcional> fim_se
     *           | caso <exp_aritmetica> seja <selecao> <senao_opcional> fim_caso
     *           | para IDENT <- <exp_aritmetica> ate <exp_aritmetica> faca <comandos> fim_para
     *           | enquanto <expressao> faca <comandos> fim_enquanto
     *           | faca <comandos> ate <expressao>
     *           | ^ IDENT <outros_ident> <dimensao> <- <expressao>
     *           | IDENT <chamada_atribuicao>
     *           | retorne <expressao>
     */
    public void cmd() {
        // leia
        if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "leia"))) {
            // ( <identificador> <mais_ident> )
            getToken();
            if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "("))) {
                getToken();
                decl = new Declaracao();
                identificador();
                Declaracao tmp = decl.clone();
                procuraDeclaracao(Declaracao.ERRO_SEMANTICO_3); // erro semantico: identificador nao declarado
                decl = tmp.clone();
                geraCodigoLeia();
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ","))) {
                    do {
                        getToken();
                        decl = new Declaracao();
                        identificador(); // analise semantica: atribuicao do identificador em decl
                        procuraDeclaracao(Declaracao.ERRO_SEMANTICO_3); // erro semantico: identificador em decl nao declarado
                        geraCodigoLeia();
                    } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ",")));
                }
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ")"))) {
                    getToken();
                } else {
                    erro();
                }
            } else {
                erro();
            }
            // escreva
        } else if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "escreva"))) {
            getToken();
            if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "("))) {
                getToken();
                // ( <expressao> <mais_expressao> )
                decl = new Declaracao();
                expressao(); // analise semantica: atribuicao da expressao em decl
                geraCodigoEscreva();
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ","))) {
                    do {
                        getToken();
                        decl = new Declaracao();
                        expressao(); // analise semantica: atribuicao da expressao em decl
                        geraCodigoEscreva();
                    } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ",")));
                }
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ")"))) {
                    getToken();
                } else {
                    erro();
                }
            } else {
                erro();
            }
            // se <expressao> entao  
        } else if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "se"))) {
            getToken();
            decl = new Declaracao();
            expressao(); // analise semantica: atribuicao da expressao em decl
            geraCodigoSe();
            if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "entao"))) {
                getToken();
                // <comandos>
                while (primeiro(CMD)) {
                    cmd();
                }
                sb.append("}\n");
                // <senao_opcional> 
                if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "senao"))) {
                    getToken();
                    geraCodigoSenao();
                    while (primeiro(CMD)) {
                        cmd();
                    }
                    sb.append("}\n");
                }
                // fim_se
                if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "fim_se"))) {
                    getToken();
                } else {
                    erro();
                }
            } else {
                erro();
            }
            // caso <exp_aritmetica>   
        } else if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "caso"))) {
            getToken();
            decl = new Declaracao();
            exp_aritmetica(); // analise semantica: atribuicao da expressao aritmetica em decl
            geraCodigoCaso();
            // seja 
            if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "seja"))) {
                getToken();
                // <selecao>
                Declaracao tmp = decl.clone(); // guarda declaracao atual
                do {
                    decl = new Declaracao();
                    constantes(); // analise semantica: atribuicao da constante em decl
                    if (!tmp.getTipo().equals(decl.getTipo())) { // analise semantica: se tipo declaracao atual diferente tipo constante
                        erroSemantico(Declaracao.ERRO_SEMANTICO_5); // erro semantico: tipos invalidos na atribuicao (caso avalia apenas inteiros)
                    }
                    if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ":"))) {
                        getToken();
                        geraCodigoSeja();
                        while (primeiro(CMD)) {
                            cmd();
                        }
                        sb.append("break;\n");
                    } else {
                        erro();
                    }
                } while (primeiro(CONSTANTES));
                decl = tmp.clone(); // recupera declaracao atual em decl
                // <senao_opcional>
                if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "senao"))) {
                    getToken();
                    sb.append("default: \n");
                    while (primeiro(CMD)) {
                        cmd();
                    }
                }
                // fim_caso
                if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "fim_caso"))) {
                    sb.append("}\n");
                    getToken();
                } else {
                    erro();
                }
            } else {
                erro();
            }
            // para IDENT
        } else if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "para"))) {
            getToken();
            decl = new Declaracao();
            if (token.getType() == SimpleToken.IDENTIFICADOR) {
                decl.setEscopo("local");
                decl.setIdentificador(token.getText());
                if (pe.lastElement().containsDeclaracao(decl)) { // analise semantica: se variavel de controle do laco ja foi declarada
                    decl = pe.lastElement().getDeclaracao(decl).clone(); // recupera variavel declarada a ser utilizada no laco
                }
                getToken();
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "<-"))) {
                    getToken();
                    // <exp_aritmetica>
                    Declaracao tmp = decl.clone();
                    exp_aritmetica();
                    if (tmp.getTipo().isEmpty()) { // analise semantica: se variavel do laco nao foi declarada anteriormente
                        geraCodigoPara(tmp, decl);
                        tmp.setTipo(decl.getTipo()); // definicao do tipo da variavel do laço                        
                    } else {
                        geraCodigoPara(tmp, decl);
                    }
                    decl = tmp.clone();
                    if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "ate"))) {
                        getToken();
                        tmp = decl.clone();
                        exp_aritmetica();
                        geraCodigoAte(tmp, decl);
                        decl = tmp.clone();
                        if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "faca"))) {
                            getToken();
                            while (primeiro(CMD)) {
                                cmd();
                            }
                            if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "fim_para"))) {
                                sb.append("}\n");
                                getToken();
                            } else {
                                erro();
                            }
                        } else {
                            erro();
                        }
                    } else {
                        erro();
                    }
                } else {
                    erro();
                }
            } else {
                erro();
            }
            // enquanto
        } else if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "enquanto"))) {
            getToken();
            decl = new Declaracao();
            expressao();
            geraCodigoEnquanto();
            if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "faca"))) {
                getToken();
                while (primeiro(CMD)) {
                    cmd();
                }
                if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "fim_enquanto"))) {
                    sb.append("}\n");
                    getToken();
                } else {
                    erro();
                }
            } else {
                erro();
            }
            // faca
        } else if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "faca"))) {
            getToken();
            sb.append("do {\n");
            while (primeiro(CMD)) {
                cmd();
            }
            if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "ate"))) {
                getToken();
                decl = new Declaracao();
                expressao();
                geraCodigoFaca();
            } else {
                erro();
            }
            // ^ IDENT
        } else if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "^"))) {
            getToken();
            decl = new Declaracao();
            decl.setPtr(true);
            if (token.getType() == SimpleToken.IDENTIFICADOR) {
                decl.setIdentificador(token.getText());
                decl.setLinha(token.getLine());
                Declaracao tmp = decl.clone();
                if (!procuraDeclaracao(Declaracao.ERRO_SEMANTICO_3)) { // erro semantico: identificador nao declarado
                    decl = tmp.clone();
                    decl.setErro(true);
                }
                decl.setLinha(token.getLine());
                if (tmp.isPtr()) {
                    decl.setIdentificador("^" + decl.getIdentificador());
                    decl.setPtr(true);
                }
                getToken();
                // <outros_ident>
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "."))) {
                    String declIdent = decl.getIdentificador();
                    do {
                        declIdent = declIdent + token.getText();
                        getToken();
                        if (token.getType() == SimpleToken.IDENTIFICADOR) {
                            // analise semantica: campos do registro
                            declIdent = declIdent + token.getText();
                            tmp = new Declaracao();
                            tmp.setEscopo("local");
                            tmp.setIdentificador(token.getText());
                            tmp.setLinha(token.getLine());
                            if (!decl.getDecLocais().contains(tmp)) {
                                decl.setIdentificador(declIdent);
                                erroSemantico(Declaracao.ERRO_SEMANTICO_3); // erro semantico: identificador nao declarado no registro
                                decl.setErro(true);
                            } else {
                                decl = decl.getDecLocais().get(decl.getDecLocais().indexOf(tmp)).clone();
                                decl.setIdentificador(declIdent);
                                decl.setLinha(token.getLine());
                            }
                            getToken();
                        } else {
                            erro();
                        }
                    } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ".")));
                }
                // <dimensao>
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "["))) {
                    tmp = decl.clone();
                    int i = 0;
                    do {
                        getToken();
                        decl = new Declaracao();
                        exp_aritmetica();
                        if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "]"))) {
                            tmp.setIdentificador(tmp.getIdentificador() + "[" + decl.getValor().trim() + "]");
                            getToken();
                        } else {
                            erro();
                        }
                    } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "[")));
                    decl = tmp.clone();
                }
                decl.setValor(decl.getValor() + " " + decl.getIdentificador().replace("^", "*"));
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "<-"))) {
                    getToken();
                    tmp = decl.clone();
                    decl = new Declaracao();
                    expressao();
                    if (!tmp.getTipo().equals(decl.getTipo())) {
                        tmp.setLinha(decl.getLinha());
                        decl = tmp.clone();
                        erroSemantico(Declaracao.ERRO_SEMANTICO_5); // erro semantico: atribuicao invalida para declaracao
                    } else {
                        geraCodigoAtribuicao(tmp, decl);
                    }
                } else {
                    erro();
                }
            }
            // IDENT
        } else if (token.getType() == SimpleToken.IDENTIFICADOR) {
            decl = new Declaracao();
            decl.setIdentificador(token.getText());
            decl.setLinha(token.getLine());
            Declaracao tmp = decl.clone();
            if (!procuraDeclaracao(Declaracao.ERRO_SEMANTICO_3)) { // erro semantico: identificador nao declarado
                decl = tmp.clone();
                decl.setErro(true);
            }
            decl.setLinha(token.getLine());
            getToken();
            if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "("))) {
                decl.setValor(decl.getValor() + " " + decl.getIdentificador() + "(");
                getToken();
                tmp = decl.clone(); // guarda declaracao atual: procedimento ou funcao
                decl = new Declaracao();
                expressao();
                try {
                    if (!tmp.getParametro().get(0).getTipo().equals(decl.getTipo())) { // se tipo do parametro diferente do tipo do argumento
                        decl = tmp.clone();
                        decl.setLinha(token.getLine());
                        erroSemantico(Declaracao.ERRO_SEMANTICO_4); // erro semantico: erro na passagem de parametros
                        decl.setErro(true);
                        tmp = decl.clone();
                    } else {
                        if (tmp.getParametro().get(0).isPtr()) {
                            decl.setValor("&" + decl.getValor().trim());
                        }
                        tmp.setValor(tmp.getValor() + decl.getValor().trim());
                        decl = tmp.clone();
                    }
                } catch (Exception e) { // declaracao global nao contem parametros
                    decl = tmp.clone();
                    decl.setLinha(token.getLine());
                    erroSemantico(Declaracao.ERRO_SEMANTICO_4); // erro semantico: erro na passagem de parametros
                    decl.setErro(true);
                    tmp = decl.clone();
                }
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ","))) {
                    int i = 1;
                    do {
                        getToken();
                        decl = new Declaracao();
                        expressao();
                        try {
                            if (!tmp.getParametro().get(i).getTipo().equals(decl.getTipo())) { // se tipo do parametro diferente do tipo do argumento
                                decl = tmp.clone();
                                decl.setLinha(token.getLine());
                                erroSemantico(Declaracao.ERRO_SEMANTICO_4); // erro semantico: erro na passagem de parametros
                                decl.setErro(true);
                                tmp = decl.clone();
                            } else {
                                if (tmp.getParametro().get(i).isPtr()) {
                                    decl.setValor("&" + decl.getValor().trim());
                                }
                                tmp.setValor(tmp.getValor() + " ," + decl.getValor().trim());
                                decl = tmp.clone();
                            }
                        } catch (Exception e) { // tentando passar mais parametros que o definido
                            decl = tmp.clone();
                            decl.setLinha(token.getLine());
                            erroSemantico(Declaracao.ERRO_SEMANTICO_4); // erro semantico: erro na passagem de parametros
                            decl.setErro(true);
                            tmp = decl.clone();
                        }
                        i++;
                    } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ",")));
                    if (i != decl.getParametro().size()) { // tentando passar menos parametros que o definido
                        erroSemantico(Declaracao.ERRO_SEMANTICO_4); // erro semantico: erro na passagem de parametros
                    }
                } else {
                    if (decl.getParametro().size() > 1) { // tentando passar menos parametros que o definido
                        erroSemantico(Declaracao.ERRO_SEMANTICO_4); // erro semantico: erro na passagem de parametros
                    }
                }
                decl = tmp.clone(); // recupera declaracao global
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ")"))) {
                    decl.setValor(decl.getValor() + ")");
                    geraCodigoChamada();
                    getToken();
                } else {
                    erro();
                }
            } else {
                // <outros_ident>
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "."))) {
                    String declIdent = decl.getIdentificador();
                    do {
                        declIdent = declIdent + token.getText();
                        getToken();
                        if (token.getType() == SimpleToken.IDENTIFICADOR) {
                            declIdent = declIdent + token.getText();
                            tmp = new Declaracao();
                            tmp.setEscopo("local");
                            tmp.setIdentificador(token.getText());
                            tmp.setLinha(token.getLine());
                            if (!decl.getDecLocais().contains(tmp)) {
                                decl.setIdentificador(declIdent);
                                erroSemantico(Declaracao.ERRO_SEMANTICO_3); // erro semantico: campo não declarado no registro
                                decl.setErro(true);
                            } else {
                                decl = decl.getDecLocais().get(decl.getDecLocais().indexOf(tmp)).clone(); // recupera campo do registro em decl
                                decl.setIdentificador(declIdent);
                                decl.setLinha(token.getLine());
                            }
                            getToken();
                        } else {
                            erro();
                        }
                    } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ".")));
                }
                // <dimensao>
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "["))) {
                    tmp = decl.clone();
                    do {
                        getToken();
                        decl = new Declaracao();
                        exp_aritmetica();
                        if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "]"))) {
                            tmp.setIdentificador(tmp.getIdentificador() + "[" + decl.getValor().trim() + "]");
                            getToken();
                        } else {
                            erro();
                        }
                    } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "[")));
                    decl = tmp.clone();
                }
                decl.setValor(decl.getValor() + " " + decl.getIdentificador());
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "<-"))) {
                    getToken();
                    tmp = decl.clone();
                    decl = new Declaracao();
                    expressao();
                    if (!(tmp.getTipo().equals(decl.getTipo()))) { // se tipo da declaracao diferente do tipo da atribuicao
                        if (!(tmp.getTipo().equals("real") & decl.getTipo().equals("inteiro"))) { // permite atribuicao de inteiros para reais
                            tmp.setLinha(decl.getLinha());
                            decl = tmp.clone();
                            erroSemantico(Declaracao.ERRO_SEMANTICO_5); // erro semantico: atribuicao invalida
                        } else {
                            geraCodigoAtribuicao(tmp, decl);
                        }
                    } else {
                        geraCodigoAtribuicao(tmp, decl);
                    }
                } else {
                    erro();
                }
            }
            // retorne
        } else if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "retorne"))) {
            getToken();
            decl = new Declaracao();
            expressao();
            geraCodigoRetorne();
            if (!pe.lastElement().getTipoRetorno().equals(decl.getTipo())) { // se tipo da expressao eh diferente do tipo de retorno do escopo
                erroSemantico(Declaracao.ERRO_SEMANTICO_6); // erro semantico: comando retorne não permitido no escopo / tipo de retorno invalido
            }
        } else {
            erro();
        }
    }

    /*
     * <expressao> ::= <termo_logico> <outros_termos_logicos>
     */
    public void expressao() {
        // <termo_logico>
        termo_logico();
        // <outros_termos_logicos>
        if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "ou"))) {
            do {
                decl.setValor("(" + decl.getValor().trim() + " ||");                
                getToken();
                termo_logico();
                decl.setValor(decl.getValor().trim() + ")");
                decl.setTipo("logico");
                decl.setLinha(token.getLine());
            } while (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "ou")));
        }
    }

    /*
     * <termo_logico> ::= <fator_logico> <outros_fatores_logicos>
     */
    public void termo_logico() {
        // <fator_logico>
        if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "nao"))) {
            decl.setValor(decl.getValor() + " !");
            getToken();
        }
        if (primeiro(TERMO_LOGICO)) {
            if (primeiro(EXP_RELACIONAL)) {
                exp_relacional();
            } else {
                decl.setTipo("logico");
                decl.setLinha(token.getLine());
                getToken();
            }
            // <outros_fatores_logicos>
            if (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "e"))) {
                do {
                    decl.setValor(decl.getValor() + " &&");
                    getToken();
                    termo_logico();
                    decl.setTipo("logico");
                    decl.setLinha(token.getLine());
                } while (token.equals(lex.getTokenReservado(SimpleToken.PALAVRA_RESERVADA, "e")));
            }
        } else {
            erro();
        }
    }

    /*
     * <constantes> ::= <numero_intervalo> <mais_constantes>
     */
    public void constantes() {
        // <numero_intervalo>
        if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "-"))) {
            decl.setValor(decl.getValor() + token.getText());
            getToken();
        }
        if (token.getType() == SimpleToken.NUMERO_INTEIRO) {
            decl.setTipo("inteiro");
            decl.setValor(decl.getValor() + token.getText());
            getToken();
        } else {
            erro();
        }
        // <intervalo_opcional>
        if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ".."))) {
            decl.setValor(decl.getValor() + token.getText());
            getToken();
            if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "-"))) {
                decl.setValor(decl.getValor() + token.getText());
                getToken();
            }
            if (token.getType() == SimpleToken.NUMERO_INTEIRO) {
                decl.setValor(decl.getValor() + token.getText());
                getToken();
            } else {
                erro();
            }
        }
        // <mais_constantes>
        if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ","))) {
            do {
                decl.setValor(decl.getValor() + token.getText());
                getToken();
                constantes();
            } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ",")));
        }
    }

    /*
     * <exp_aritmetica> ::= <termo> <outros_termos>
     */
    public void exp_aritmetica() {
        // <termo>
        termo();
        // <outros_termos>
        if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "+"))
                | token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "-"))) {
            do {
                decl.setValor("(" + decl.getValor().trim() + " " + token.getText());
                getToken();
                termo();
                decl.setValor(decl.getValor().trim() + ")");
            } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "+"))
                    | token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "-")));
        }
    }

    /*
     * <termo> ::= <fator> <outros_fatores>
     */
    public void termo() {
        // <fator>
        fator();
        // <outros_fatores>
        if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "*"))
                | token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "/"))) {
            do {
                decl.setValor(decl.getValor() + " " + token.getText());
                getToken();
                fator();
            } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "*"))
                    | token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "/")));
        }
    }

    /*
     * <fator> ::= <parcela> <outras_parcelas>
     */
    public void fator() {
        if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "-"))) {
            decl.setValor(decl.getValor() + " " + token.getText());
            getToken();
            parcela_unario();
        } else if (primeiro(PARCELA_UNARIO)) {
            parcela_unario();
        } else if (primeiro(PARCELA_NAO_UNARIO)) {
            parcela_nao_unario();
        } else {
            erro();
        }
        while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "%"))) {
            decl.setValor(decl.getValor() + " " + token.getText());
            getToken();
            fator();
        }
    }

    /*
     * <parcela_unario> ::= ^ IDENT <outros_ident> <dimensao> | IDENT <chamada_partes> | NUM_INT | NUM_REAL | ( <expressao> )
     */
    public void parcela_unario() {
        // ^ IDENT
        if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "^"))) {
            decl.setPtr(true);
            getToken();
            if (token.getType() == SimpleToken.IDENTIFICADOR) {
                decl.setIdentificador(token.getText());
                decl.setLinha(token.getLine());
                Declaracao tmp = decl.clone();
                if (!procuraDeclaracao(Declaracao.ERRO_SEMANTICO_3)) {
                    decl = tmp.clone();
                    decl.setErro(true);
                }
                decl.setLinha(token.getLine());
                if (tmp.isPtr()) {
                    decl.setIdentificador("^" + decl.getIdentificador());
                    decl.setPtr(true);
                }
                getToken();
                // <outros_ident>
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "."))) {
                    String declIdent = decl.getIdentificador();
                    do {
                        declIdent = declIdent + token.getText();
                        getToken();
                        if (token.getType() == SimpleToken.IDENTIFICADOR) {
                            declIdent = declIdent + token.getText();
                            tmp = new Declaracao();
                            tmp.setEscopo("local");
                            tmp.setIdentificador(token.getText());
                            tmp.setLinha(token.getLine());
                            if (!decl.getDecLocais().contains(tmp)) {
                                decl.setIdentificador(declIdent);
                                erroSemantico(Declaracao.ERRO_SEMANTICO_3);
                                decl.setErro(true);
                            } else {
                                decl = decl.getDecLocais().get(decl.getDecLocais().indexOf(tmp)).clone();
                                decl.setIdentificador(declIdent);
                                decl.setLinha(token.getLine());
                            }
                            getToken();
                        } else {
                            erro();
                        }
                    } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ".")));
                }
                // <dimensao>
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "["))) {
                    tmp = decl.clone();
                    do {
                        getToken();
                        decl = new Declaracao();
                        exp_aritmetica();
                        if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "]"))) {
                            tmp.setIdentificador(tmp.getIdentificador() + "[" + decl.getValor().trim() + "]");
                            getToken();
                        } else {
                            erro();
                        }
                    } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "[")));
                    decl = tmp.clone();
                }
            } else {
                erro();
            }
            if (decl.isPtr()) {
                decl.setValor(decl.getValor() + " " + decl.getIdentificador());
            } else {
                decl.setValor(decl.getValor() + " " + decl.getIdentificador().replace("^", "*"));
            }
            // | IDENT
        } else if (token.getType() == SimpleToken.IDENTIFICADOR) {
            decl.setIdentificador(token.getText());
            decl.setLinha(token.getLine());
            getToken();
            Declaracao tmp = decl.clone();
            if (!procuraDeclaracao(Declaracao.ERRO_SEMANTICO_3)) {
                decl = tmp.clone();
                decl.setErro(true);
            }
            decl.setLinha(token.getLine());
            decl.setValor(tmp.getValor());
            // <chamada_partes>
            if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "("))) {
                decl.setValor(decl.getValor() + " " + decl.getIdentificador() + "(");
                getToken();
                tmp = decl.clone();
                decl = new Declaracao();
                expressao();
                try {
                    if (!tmp.getParametro().get(0).getTipo().equals(decl.getTipo())) {
                        decl = tmp.clone();
                        decl.setLinha(token.getLine());
                        erroSemantico(Declaracao.ERRO_SEMANTICO_4);
                        decl.setErro(true);
                        tmp = decl.clone();
                    } else {
                        tmp.setValor(tmp.getValor() + decl.getValor().trim());
                        decl = tmp.clone();
                    }
                } catch (Exception e) {
                    decl = tmp.clone();
                    decl.setLinha(token.getLine());
                    erroSemantico(Declaracao.ERRO_SEMANTICO_4);
                    decl.setErro(true);
                    tmp = decl.clone();
                }
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ","))) {
                    int i = 1;
                    do {
                        getToken();
                        decl = new Declaracao();
                        expressao();
                        try {
                            if (!tmp.getParametro().get(i).getTipo().equals(decl.getTipo())) {
                                decl = tmp.clone();
                                decl.setLinha(token.getLine());
                                erroSemantico(Declaracao.ERRO_SEMANTICO_4);
                                decl.setErro(true);
                                tmp = decl.clone();
                            } else {
                                tmp.setValor(tmp.getValor() + " ," + decl.getValor().trim());
                                decl = tmp.clone();
                            }
                        } catch (Exception e) {
                            decl = tmp.clone();
                            decl.setLinha(token.getLine());
                            erroSemantico(Declaracao.ERRO_SEMANTICO_4);
                            decl.setErro(true);
                            tmp = decl.clone();
                        }
                        i++;
                    } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ",")));
                    if (i != decl.getParametro().size()) {
                        erroSemantico(Declaracao.ERRO_SEMANTICO_4);
                    }
                } else {
                    if (decl.getParametro().size() > 1) {
                        erroSemantico(Declaracao.ERRO_SEMANTICO_4);
                    }
                }
                decl = tmp.clone();
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ")"))) {
                    decl.setValor(decl.getValor() + ")");
                    getToken();
                } else {
                    erro();
                }
            } else {
                // <outros_ident>
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "."))) {
                    String declIdent = decl.getIdentificador();
                    do {
                        declIdent = declIdent + token.getText();
                        getToken();
                        if (token.getType() == SimpleToken.IDENTIFICADOR) {
                            declIdent = declIdent + token.getText();
                            tmp = new Declaracao();
                            tmp.setEscopo("local");
                            tmp.setIdentificador(token.getText());
                            tmp.setLinha(token.getLine());
                            if (!decl.getDecLocais().contains(tmp)) {
                                decl.setIdentificador(declIdent);
                                erroSemantico(Declaracao.ERRO_SEMANTICO_3);
                                decl.setErro(true);
                            } else {
                                String valor = decl.getValor();
                                decl = decl.getDecLocais().get(decl.getDecLocais().indexOf(tmp)).clone();
                                decl.setIdentificador(declIdent);
                                decl.setLinha(token.getLine());
                                decl.setValor(valor);
                            }
                            getToken();
                        } else {
                            erro();
                        }
                    } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ".")));
                }
                // <dimensao>
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "["))) {
                    tmp = decl.clone();
                    do {
                        getToken();
                        decl = new Declaracao();
                        exp_aritmetica();
                        if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "]"))) {
                            tmp.setIdentificador(tmp.getIdentificador() + "[" + decl.getValor().trim() + "]");
                            getToken();
                        } else {
                            erro();
                        }
                    } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "[")));
                    decl = tmp.clone();
                }
                decl.setValor(decl.getValor() + " " + decl.getIdentificador());
            }
            // NUM_INT
        } else if (token.getType() == SimpleToken.NUMERO_INTEIRO) {
            decl.setTipo("inteiro");
            decl.setValor(decl.getValor() + " " + token.getText());
            decl.setLinha(token.getLine());
            getToken();
            // NUM_REAL
        } else if (token.getType() == SimpleToken.NUMERO_REAL) {
            decl.setTipo("real");
            decl.setValor(decl.getValor() + " " + token.getText());
            decl.setLinha(token.getLine());
            getToken();
            // ( <expressao> )
        } else if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "("))) {
            getToken();
            expressao();
            if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ")"))) {
                getToken();
            } else {
                erro();
            }
        } else {
            erro();
        }
    }

    /*
     * <parcela_nao_unario> ::= & IDENT <outros_ident> <dimensao> | CADEIA
     */
    public void parcela_nao_unario() {
        // & IDENT
        if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "&"))) {
            decl.setPtr(true);
            getToken();
            if (token.getType() == SimpleToken.IDENTIFICADOR) {
                decl.setIdentificador(token.getText()); // analise semantica
                decl.setLinha(token.getLine());
                Declaracao tmp = decl.clone();
                if (!procuraDeclaracao(Declaracao.ERRO_SEMANTICO_3)) {
                    decl = tmp.clone();
                    decl.setErro(true);
                }
                decl.setLinha(token.getLine());
                if (tmp.isPtr()) {
                    decl.setIdentificador("&" + decl.getIdentificador());
                    decl.setPtr(true);
                }
                getToken();
                // <outros_ident>
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "."))) {
                    String declIdent = decl.getIdentificador();
                    do {
                        declIdent = declIdent + token.getText();
                        getToken();
                        if (token.getType() == SimpleToken.IDENTIFICADOR) {
                            declIdent = declIdent + token.getText();
                            tmp = new Declaracao();
                            tmp.setEscopo("local");
                            tmp.setIdentificador(token.getText());
                            tmp.setLinha(token.getLine());
                            if (!decl.getDecLocais().contains(tmp)) {
                                decl.setIdentificador(declIdent);
                                erroSemantico(Declaracao.ERRO_SEMANTICO_3);
                                decl.setErro(true);
                            } else {
                                decl = decl.getDecLocais().get(decl.getDecLocais().indexOf(tmp)).clone();
                                decl.setIdentificador(declIdent);
                                decl.setLinha(token.getLine());
                            }
                            getToken();
                        } else {
                            erro();
                        }
                    } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ".")));
                }
                // <dimensao>
                if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "["))) {
                    tmp = decl.clone();
                    do {
                        getToken();
                        decl = new Declaracao();
                        exp_aritmetica();
                        if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "]"))) {
                            tmp.setIdentificador(tmp.getIdentificador() + "[" + decl.getValor().trim() + "]");
                            getToken();
                        } else {
                            erro();
                        }
                    } while (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "[")));
                    decl = tmp.clone();
                }
                decl.setValor(decl.getValor() + " " + decl.getIdentificador());
            }
            // CADEIA
        } else if (token.getType() == SimpleToken.CADEIA_LITERAL) {
            decl.setTipo("literal");
            decl.setValor(decl.getValor() + " " + token.getText());
            decl.setLinha(token.getLine());
            getToken();
        } else {
            erro();
        }
    }

    /*
     * <exp_relacional> ::= <exp_aritmetica> <op_opcional>
     */
    public void exp_relacional() {
        exp_aritmetica();
        if (token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "="))
                | token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "<>"))
                | token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ">="))
                | token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "<="))
                | token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, ">"))
                | token.equals(lex.getTokenReservado(SimpleToken.SIMBOLO_RESERVADO, "<"))) {
            decl.setTipo("logico");
            if (token.getText().equals("=")) {
                decl.setValor(decl.getValor() + " ==");
            } else if (token.getText().equals("<>")) {
                decl.setValor(decl.getValor() + " !=");
            } else {
                decl.setValor(decl.getValor() + " " + token.getText());
            }
            getToken();
            exp_aritmetica();
        }
    }

    public boolean erro() {
        if (!erro) {
            try {
                switch (token.getType()) {
                    case SimpleToken.ERRO:
                        out.write("Linha " + decl.getLinha() + ": " + token.getText() + " - simbolo nao identificado");
                        out.newLine();
                        break;
                    case SimpleToken.ERRO2:
                        out.write("Linha " + decl.getLinha() + ": comentario nao fechado");
                        out.newLine();
                        break;
                    default:
                        out.write("Linha " + decl.getLinha() + ": erro sintatico proximo a " + decl.getIdentificador());
                        out.newLine();
                }
            } catch (Exception e) {
            }
        }
        erro = true;
        return false;
    }

    public void erroSemantico(int tipoErro) {
        if (!erro) {
            if (!decl.isErro()) {
                try {
                    switch (tipoErro) {
                        case Declaracao.ERRO_SEMANTICO_1:
                            out.write("Linha " + decl.getLinha() + ": identificador " + decl.getIdentificador() + " ja declarado anteriormente");
                            out.newLine();
                            decl.setErro(true);
                            erroSemantico = true;
                            break;
                        case Declaracao.ERRO_SEMANTICO_2:
                            out.write("Linha " + decl.getLinha() + ": tipo " + decl.getIdentificador() + " nao declarado");
                            out.newLine();
                            decl.setErro(true);
                            erroSemantico = true;
                            break;
                        case Declaracao.ERRO_SEMANTICO_3:
                            out.write("Linha " + decl.getLinha() + ": identificador " + decl.getIdentificador() + " nao declarado");
                            out.newLine();
                            decl.setErro(true);
                            erroSemantico = true;
                            break;
                        case Declaracao.ERRO_SEMANTICO_4:
                            out.write("Linha " + decl.getLinha() + ": incompatibilidade de parametros na chamada de " + decl.getIdentificador());
                            out.newLine();
                            decl.setErro(true);
                            erroSemantico = true;
                            break;
                        case Declaracao.ERRO_SEMANTICO_5:
                            out.write("Linha " + decl.getLinha() + ": atribuicao nao compativel para " + decl.getIdentificador());
                            out.newLine();
                            decl.setErro(true);
                            erroSemantico = true;
                            break;
                        case Declaracao.ERRO_SEMANTICO_6:
                            out.write("Linha " + decl.getLinha() + ": comando retorne nao permitido nesse escopo");
                            out.newLine();
                            decl.setErro(true);
                            erroSemantico = true;
                            break;
                        default:
                    }
                } catch (Exception e) {
                }
            }
        }
    }

    /* funções auxiliares - análise semântica */
    public boolean procuraDeclaracao(int erroSemantico) {
        if (pe.lastElement().containsDeclaracao(decl, "local")) {
            decl = pe.lastElement().getDeclaracao(decl).clone();
            return true;
        } else if (pe.lastElement().containsDeclaracao(decl, "global")) { // parametro novo escopo
            decl = pe.lastElement().getDeclaracao(decl).clone();
            return true;
        } else if (pe.firstElement().containsDeclaracao(decl, "global")) {
            decl = pe.lastElement().getDeclaracao(decl).clone();
            return true;
        } else {
            erroSemantico(erroSemantico);
            return false;
        }
    }

    public void insereDeclaracaoEscopoGlobal() {
        if (ld.isEmpty()) {
            if (pe.lastElement().containsDeclaracao(decl)) {
                erroSemantico(Declaracao.ERRO_SEMANTICO_1);
            } else {
                decl.setEscopo("global");
                pe.lastElement().addDeclaracao(decl);
                geraCodigoDeclaracao();
            }
        } else {
            for (int i = 0; i < ld.size(); i++) {
                if (pe.lastElement().containsDeclaracao(ld.get(i))) {
                    erroSemantico(Declaracao.ERRO_SEMANTICO_1);
                } else {
                    ld.get(i).setEscopo("global");
                    pe.lastElement().addDeclaracao(ld.get(i));
                    decl = ld.get(i);
                    geraCodigoDeclaracao();
                }
            }
        }
    }

    public void insereDeclaracaoEscopo() {
        if (decl.getCategoria().equals("tipo") | decl.getCategoria().equals("parametro")) {
            if (pe.lastElement().containsDeclaracao(decl)) {
                erroSemantico(Declaracao.ERRO_SEMANTICO_1);
            } else {
                pe.lastElement().addDeclaracao(decl);
                geraCodigoDeclaracao();
            }
        } else {
            if (ld.isEmpty()) {
                if (pe.lastElement().containsDeclaracao(decl)) {
                    erroSemantico(Declaracao.ERRO_SEMANTICO_1);
                } else {
                    pe.lastElement().addDeclaracao(decl);
                    geraCodigoDeclaracao();
                }
            } else {
                for (int i = 0; i < ld.size(); i++) {
                    if (pe.lastElement().containsDeclaracao(ld.get(i))) {
                        decl = ld.get(i).clone();
                        erroSemantico(Declaracao.ERRO_SEMANTICO_1);
                    } else {
                        pe.lastElement().addDeclaracao(ld.get(i));
                        decl = ld.get(i);
                        geraCodigoDeclaracao();
                    }
                }
            }
        }
    }

    public void insereDeclaracaoEscopo(Declaracao declEscopo) {
        if (ld.isEmpty()) {
            if (declEscopo.getDecLocais().contains(decl)) {
                erroSemantico(Declaracao.ERRO_SEMANTICO_1);
            } else {
                declEscopo.getDecLocais().add(decl);
            }
        } else {
            for (int i = 0; i < ld.size(); i++) {
                if (declEscopo.getDecLocais().contains(ld.get(i))) {
                    erroSemantico(Declaracao.ERRO_SEMANTICO_1);
                } else {
                    declEscopo.getDecLocais().add(ld.get(i));
                }
            }
        }
    }

    public void insereParametroEscopo() {
        for (int i = 0; i < decl.getParametro().size(); i++) {
            Declaracao param = decl.getParametro().get(i).clone();
            param.setEscopo("global");
            if (pe.lastElement().containsDeclaracao(param)) {
                erroSemantico(Declaracao.ERRO_SEMANTICO_1);
            } else {
                pe.lastElement().addDeclaracao(param);
            }
        }
    }

    public void setTipoBasicoDeclaracao(Token t) {
        switch (t.getType()) {
            case SimpleToken.NUMERO_INTEIRO:
                decl.setTipo("inteiro");
                break;
            case SimpleToken.NUMERO_REAL:
                decl.setTipo("real");
                break;
            case SimpleToken.CADEIA_LITERAL:
                decl.setTipo("literal");
                break;
            case SimpleToken.PALAVRA_RESERVADA:
                if (token.getText().equals("verdadeiro") | token.getText().equals("falso")) {
                    decl.setTipo("logico");
                }
                break;
            default:
                decl.setTipo(null);
        }
    }

    public void geraCodigoHeader() {
        sb.append("#include <stdio.h>\n"); // geracao de codigo C
        sb.append("#include <stdlib.h>\n\n");
    }

    public void geraCodigoDeclaracao() {
        if (decl.getCategoria().equals("tipo")) {
            geraCodigoTipo();
        } else if (decl.getCategoria().equals("constante")) {
            geraCodigoConstante();
        } else if (!decl.getCategoria().equals("parametro")) {
            if (decl.getTipo().equals("inteiro") | decl.getTipo().equals("logico")) {
                if (decl.isPtr()) {
                    sb.append("int* ");
                } else {
                    sb.append("int ");
                }
                if (decl.isArray()) {
                    sb.append(decl.getIdentificador());
                    for (int i = 0; i < decl.getDimensao().size(); i++) {
                        sb.append("[");
                        sb.append(decl.getDimensao().get(i).getValor().trim());
                        sb.append("]");
                    }
                } else {
                    sb.append(decl.getIdentificador());
                }
                sb.append(";\n");
            } else if (decl.getTipo().equals("real")) {
                if (decl.isPtr()) {
                    sb.append("float* ");
                } else {
                    sb.append("float ");
                }
                if (decl.isArray()) {
                    sb.append(decl.getIdentificador());
                    for (int i = 0; i < decl.getDimensao().size(); i++) {
                        sb.append("[");
                        sb.append(decl.getDimensao().get(i).getValor().trim());
                        sb.append("]");
                    }
                } else {
                    sb.append(decl.getIdentificador());
                }
                sb.append(";\n");
            } else if (decl.getTipo().equals("literal")) {
                sb.append("char ");
                sb.append(decl.getIdentificador());
                sb.append("[80];\n");
            } else if (decl.getTipo().equals("registro")) {
                geraCodigoRegistro();
            } else {
                sb.append(decl.getTipo());
                if (decl.isPtr()) {
                    sb.append("* ");
                } else {
                    sb.append(" ");
                }
                if (decl.isArray()) {
                    sb.append(decl.getIdentificador());
                    for (int i = 0; i < decl.getDimensao().size(); i++) {
                        sb.append("[");
                        sb.append(decl.getDimensao().get(i).getValor().trim());
                        sb.append("]");
                    }
                } else {
                    sb.append(decl.getIdentificador());
                }
                sb.append(";\n");
            }
        }
    }

    public void geraCodigoTipo() {
        sb.append("typedef struct {\n");
        Declaracao tmp = decl.clone();
        for (int i = 0; i < tmp.getDecLocais().size(); i++) {
            decl = tmp.getDecLocais().get(i).clone();
            geraCodigoDeclaracao();
        }
        decl = tmp.clone();
        sb.append("} ");
        sb.append(decl.getIdentificador());
        sb.append(";\n");
    }

    public void geraCodigoRegistro() {
        sb.append("struct {\n");
        Declaracao tmp = decl.clone();
        for (int i = 0; i < tmp.getDecLocais().size(); i++) {
            decl = tmp.getDecLocais().get(i).clone();
            geraCodigoDeclaracao();
        }
        decl = tmp.clone();
        sb.append("} ");
        sb.append(decl.getIdentificador());
        sb.append(";\n");
    }

    public void geraCodigoConstante() {
        sb.append("#define ");
        sb.append(decl.getIdentificador());
        sb.append(" ");
        sb.append(decl.getValor());
        sb.append("\n");
    }

    public void geraCodigoProcedimento() {
        sb.append("void ");
        sb.append(decl.getIdentificador());
        sb.append("(");
        Declaracao tmp = decl.clone();
        decl = tmp.getParametro().get(0).clone();
        geraCodigoParametro();
        if (tmp.getParametro().size() > 1) {
            for (int i = 1; i < tmp.getParametro().size(); i++) {
                sb.append(", ");
                decl = tmp.getParametro().get(i).clone();
                geraCodigoParametro();
            }
        }
        sb.append(") {\n");
    }

    public void geraCodigoFuncao() {
        if (decl.getTipo().equals("inteiro") | decl.getTipo().equals("logico")) {
            if (decl.isPtr()) {
                sb.append("int* ");
            } else {
                sb.append("int ");
            }
        } else if (decl.getTipo().equals("real")) {
            if (decl.isPtr()) {
                sb.append("float* ");
            } else {
                sb.append("float ");
            }
        } else if (decl.getTipo().equals("literal")) {
            if (decl.isPtr()) {
                sb.append("char* ");
            } else {
                sb.append("char ");
            }
        }
        sb.append(decl.getIdentificador());
        sb.append("(");
        Declaracao tmp = decl.clone();
        decl = tmp.getParametro().get(0).clone();
        geraCodigoParametro();
        if (tmp.getParametro().size() > 1) {
            for (int i = 1; i < tmp.getParametro().size(); i++) {
                sb.append(", ");
                decl = tmp.getParametro().get(i).clone();
                geraCodigoParametro();
            }
        }
        sb.append(") {\n");
    }

    public void geraCodigoParametro() {
        if (decl.getTipo().equals("inteiro")) {
            if (decl.isPtr()) {
                sb.append("int* ");
            } else {
                sb.append("int ");
            }
            sb.append(decl.getIdentificador());
        } else if (decl.getTipo().equals("real")) {
            if (decl.isPtr()) {
                sb.append("float* ");
            } else {
                sb.append("float ");
            }
            sb.append(decl.getIdentificador());
        } else if (decl.getTipo().equals("literal")) {
            sb.append("char* ");
            sb.append(decl.getIdentificador());
        } else if (decl.getTipo().equals("logico")) {
            if (decl.isPtr()) {
                sb.append("int* ");
            } else {
                sb.append("int ");
            }
            sb.append(decl.getIdentificador());
        } else if (decl.getTipo().equals("registro")) {
            if (decl.isPtr()) {
                sb.append("struct* ");
            } else {
                sb.append("struct ");
            }
            sb.append(decl.getIdentificador());
        } else {
            sb.append(decl.getTipo());
            if (decl.isPtr()) {
                sb.append("* ");
            } else {
                sb.append(" ");
            }
            sb.append(decl.getIdentificador());
        }
    }

    public void geraCodigoLeia() {
        if (decl.getTipo().equals("inteiro") | decl.getTipo().equals("logico")) {
            sb.append("scanf(\"%d\", &");
        } else if (decl.getTipo().equals("real")) {
            sb.append("scanf(\"%f\", &");
        } else if (decl.getTipo().equals("literal")) {
            sb.append("gets(");
        }
        if (decl.getValor().split("\\.")[0].equals(decl.getIdentificador())) {
            if (decl.isPtr()) {
                decl.setValor(decl.getValor().replace(".", "->"));
            }
            sb.append(decl.getValor().trim());
        } else {
            if (decl.isPtr()) {
                decl.setIdentificador("&" + decl.getIdentificador());
            }
            sb.append(decl.getIdentificador());
        }
        sb.append(");\n");
    }

    public void geraCodigoEscreva() {
        sb.append("printf(");
        if (decl.getValor().trim().equals(decl.getIdentificador())) {
            if (decl.getTipo().equals("inteiro") | decl.getTipo().equals("logico")) {
                if (decl.getValor().split("\\.")[0].matches("0-9*")) {
                    sb.append("\"%f\", ");
                } else {
                    sb.append("\"%d\", ");
                }
            } else if (decl.getTipo().equals("real")) {
                sb.append("\"%f\", ");
            } else if (decl.getTipo().equals("literal")) {
                sb.append("\"%s\", ");
            }
            if (decl.getValor().split("\\.")[0].equals(decl.getIdentificador())) {
                if (decl.isPtr()) {
                    decl.setValor(decl.getValor().replace(".", "->"));
                }
                sb.append(decl.getValor().trim());
            } else {
                sb.append(decl.getIdentificador());
            }
            sb.append(");\n");
        } else {
            if (pe.lastElement().containsDeclaracao(decl)) {
                if (decl.getTipo().equals("inteiro") | decl.getTipo().equals("logico")) {
                    if (decl.getValor().split("\\.")[0].matches("0-9*")) {
                        sb.append("\"%f\", ");
                    } else {
                        sb.append("\"%d\", ");
                    }
                } else if (decl.getTipo().equals("real")) {
                    sb.append("\"%f\", ");
                } else if (decl.getTipo().equals("literal")) {
                    sb.append("\"%s\", ");
                }
                sb.append(decl.getValor().trim());
                sb.append(");\n");
            } else if (decl.getCategoria().equals("funcao")) {
                if (decl.getTipo().equals("inteiro") | decl.getTipo().equals("logico")) {
                    sb.append("\"%d\", ");
                } else if (decl.getTipo().equals("real")) {
                    sb.append("\"%f\", ");
                } else if (decl.getTipo().equals("literal")) {
                    sb.append("\"%s\", ");
                }
                sb.append(decl.getValor().trim());
                sb.append(");\n");
            } else {
                sb.append(decl.getValor().trim());
                sb.append(");\n");
            }
        }
    }

    public void geraCodigoSe() {
        sb.append("if (");
        sb.append(decl.getValor().replace("^", "*").trim());
        sb.append(") {\n");
    }

    public void geraCodigoSenao() {
        if (token.getText().equals("se")) {
            sb.append("else ");
        } else {
            sb.append("else {\n");
        }
    }

    public void geraCodigoCaso() {
        sb.append("switch (");
        if (decl.getIdentificador().isEmpty()) {
            sb.append(decl.getValor().trim());
        } else {
            sb.append(decl.getIdentificador());
        }
        sb.append(") {\n");
    }

    public void geraCodigoSeja() {
        if (decl.getValor().contains("..")) {
            String[] interval = decl.getValor().trim().split("\\..");
            for (int i = Integer.parseInt(interval[0]); i <= Integer.parseInt(interval[1]); i++) {
                sb.append("case ");
                sb.append(i);
                sb.append(":\n");
            }
        } else {
            sb.append("case ");
            sb.append(decl.getValor().trim());
            sb.append(":\n");
        }
    }

    public void geraCodigoPara(Declaracao esq, Declaracao dir) {
        sb.append("for (");
        if (esq.getTipo().isEmpty()) {
            if (dir.getTipo().equals("inteiro") | (dir.getTipo().equals("logico"))) {
                sb.append("int ");
                sb.append(esq.getIdentificador());
                sb.append(" = ");
                sb.append(dir.getValor().trim());
                sb.append(";");
            } else if (dir.getTipo().equals("real")) {
                sb.append("float ");
                sb.append(esq.getIdentificador());
                sb.append(" = ");
                sb.append(dir.getValor().trim());
                sb.append(";");
            } else if (dir.getTipo().equals("literal")) {
                sb.append("char ");
                sb.append(esq.getIdentificador());
                sb.append("[80]");
                sb.append(" = ");
                sb.append(dir.getValor().trim());
                sb.append("; ");
            }
        } else {
            sb.append(esq.getIdentificador());
            sb.append(" = ");
            sb.append(dir.getValor().trim());
            sb.append("; ");
        }
    }

    public void geraCodigoAte(Declaracao esq, Declaracao dir) {
        sb.append(esq.getIdentificador());
        sb.append(" <= ");
        sb.append(dir.getValor().trim());
        sb.append("; ");
        sb.append(esq.getIdentificador());
        sb.append("++) {\n");
    }

    public void geraCodigoEnquanto() {
        sb.append("while (");
        sb.append(decl.getValor().trim());
        sb.append(") {\n");
    }

    public void geraCodigoFaca() {
        sb.append("} while(!(");
        sb.append(decl.getValor().trim());
        sb.append("));\n");
    }

    public void geraCodigoRetorne() {
        sb.append("return ");
        sb.append(decl.getValor().trim());
        sb.append(";\n");
    }

    public void geraCodigoAtribuicao(Declaracao esq, Declaracao dir) {
        esq.setValor(esq.getValor().replace("^", "*"));
        dir.setValor(dir.getValor().replace("^", "*"));
        if (esq.getTipo().equals("literal")) {
            if (dir.getValor().contains("+")) {
                String[] exp = dir.getValor().split("\\+");
                for (int i = 0; i < exp.length; i++) {
                    sb.append("strcat(");
                    sb.append(esq.getValor().trim());
                    sb.append(", ");
                    sb.append(exp[i].trim());
                    sb.append(");\n");
                }
            } else {
                sb.append("strcpy(");
                sb.append(esq.getValor().trim());
                sb.append(", ");
                sb.append(dir.getValor().trim());
                sb.append(");\n");
            }
        } else {
            if (esq.isPtr()) {
                esq.setValor(esq.getValor().replace(".", "->"));
                dir.setValor(dir.getValor().replace(".", "->"));
            } else if (dir.isPtr()) {
                dir.setValor(dir.getValor().replace(".", "->"));
            }
            sb.append(esq.getValor().trim());
            sb.append(" = ");            
            sb.append(dir.getValor().trim());
            sb.append(";\n");
        }
    }

    public void geraCodigoChamada() {
        sb.append(decl.getValor().trim());
        sb.append(";\n");
    }

    public void includeBasicos() {
        /* char *subLiteral(char *str, int inicio, int comprimento) */
        decl = new Declaracao("global", "funcao", "subLiteral", "literal", true, false);
        Declaracao param = new Declaracao("local", "parametro", "str", "literal", true, false);
        decl.getParametro().add(param);
        param = new Declaracao("local", "parametro", "inicio", "inteiro", false, false);
        decl.getParametro().add(param);
        param = new Declaracao("local", "parametro", "comprimento", "inteiro", false, false);
        decl.getParametro().add(param);
        Declaracao decLocal = new Declaracao("local", "variavel", "sub", "literal", false, true);
        decl.getDecLocais().add(decLocal);
        decLocal = new Declaracao("local", "variavel", "i", "inteiro", false, false);
        decl.getDecLocais().add(decLocal);
        pe.firstElement().addDeclaracao(decl);
        decl = new Declaracao();

        /* void limpa_entrada() */
        decl = new Declaracao("global", "procedimento", "limpa_entrada", "", false, false);
        decLocal = new Declaracao("local", "variavel", "byte", "literal", false, true);
        decl.getDecLocais().add(decLocal);
        pe.firstElement().addDeclaracao(decl);
        decl = new Declaracao();

        /* double sen(double angulo) */
        decl = new Declaracao("global", "funcao", "sen", "real", false, false);
        param = new Declaracao("local", "parametro", "angulo", "real", false, false);
        decl.getParametro().add(param);
        pe.firstElement().addDeclaracao(decl);
        decl = new Declaracao();

        /* double pot(double base, double expoente) */
        decl = new Declaracao("global", "funcao", "pot", "real", false, false);
        param = new Declaracao("local", "parametro", "base", "real", false, false);
        decl.getParametro().add(param);
        param = new Declaracao("local", "parametro", "expoente", "real", false, false);
        decl.getParametro().add(param);
        pe.firstElement().addDeclaracao(decl);

        /* int trunca(double valor) */
        decl = new Declaracao("global", "funcao", "trunca", "inteiro", false, false);
        param = new Declaracao("local", "parametro", "valor", "real", false, false);
        decl.getParametro().add(param);
        pe.firstElement().addDeclaracao(decl);

        /* double frac(double valor) */
        decl = new Declaracao("global", "funcao", "frac", "real", false, false);
        param = new Declaracao("local", "parametro", "valor", "real", false, false);
        decl.getParametro().add(param);
        pe.firstElement().addDeclaracao(decl);
    }
}